﻿using System;
using System.Collections.Concurrent;
using System.IO;
using System.Reflection;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.DBIBase
{
    /// <summary>
    /// 数据库访问类管理器
    /// </summary>
    public class DBAccessManager
    {
        /// <summary>
        /// 数据库访问对象实例字典[key:数据库编号ID;value:数据库访问实例]
        /// </summary>
        private static readonly ConcurrentDictionary<int, IDBAccess> _dbAccessDic = new ConcurrentDictionary<int, IDBAccess>();

        /// <summary>
        /// 线程锁
        /// </summary>
        private static readonly object _dicDBAccessLock = new object();

        /// <summary>
        /// 获取数据库访问实例
        /// </summary>
        /// <param name="dbid">数据库编号ID</param>
        /// <returns>数据库访问实例</returns>
        public static IDBAccess GetDBAccessInstance(int dbid)
        {
            IDBAccess dbAccess;
            if (!_dbAccessDic.TryGetValue(dbid, out dbAccess))
            {
                lock (_dicDBAccessLock)
                {
                    if (!_dbAccessDic.TryGetValue(dbid, out dbAccess))
                    {
                        var dbBConfigItem = DatabaseConfigManager.GetConfig(dbid);
                        dbAccess = PrimitiveCreateDBAccessInstance(dbBConfigItem);
                    }
                }
            }

            return dbAccess;
        }

        /// <summary>
        /// 创建数据库访问实例
        /// </summary>
        /// <param name="dbConfig">数据库配置</param>
        /// <returns>数据库访问实例</returns>
        public static IDBAccess CreateDBAccessInstance(DatabaseConfig dbConfig)
        {
            if (dbConfig == null)
            {
                throw new ArgumentNullException(nameof(dbConfig));
            }

            lock (_dicDBAccessLock)
            {
                return PrimitiveCreateDBAccessInstance(dbConfig);
            }
        }

        /// <summary>
        /// 创建数据库访问实例
        /// </summary>
        /// <param name="dbConfig">数据库配置</param>
        /// <returns>数据库访问实例</returns>
        private static IDBAccess PrimitiveCreateDBAccessInstance(DatabaseConfig dbConfig)
        {
            IDBAccess dbAccess;
            if (_dbAccessDic.TryGetValue(dbConfig.DBID, out dbAccess))
            {
                throw new ArgumentException(string.Format("数据库编号ID为{0}数据库访问实例已存在", dbConfig.DBID));
            }

            object key = dbConfig.DBID;
            var enable = Ex.Base.AssemblyEx.Enable;
            try
            {
                Ex.Base.AssemblyEx.Enable = true;
                //Ex.Base.AssemblyEx.AddManualLoadAssemblyFunc(key, ManualLoadAssemblyFunc);
                IDBFactory dbFactory = DBFactoryManager.GetDBFactory(dbConfig);
                dbAccess = dbFactory.CreateDBAccess(dbConfig);
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                //Ex.Base.AssemblyEx.RemoveManualLoadAssemblyFunc(key);
                Ex.Base.AssemblyEx.Enable = enable;
            }


            if (!_dbAccessDic.TryAdd(dbConfig.DBID, dbAccess))
            {
                ZLoger.Warn(string.Format("添加数据库编号ID为{0}数据库访问实例失败", dbConfig.DBID), null);
            }

            return dbAccess;
        }

        //private static System.Reflection.Assembly ManualLoadAssemblyFunc(object key, object sender, ResolveEventArgs args)
        //{
        //    Assembly assembly = null;
        //    if (args.RequestingAssembly != null)
        //    {
        //        try
        //        {
        //            var dbid = (int)key;
        //            var dbConfig = DatabaseConfigManager.GetConfig(dbid);
        //            IDBFactory dbFactory = DBFactoryManager.GetDBFactory(dbConfig);
        //            string dbPluginDir = PathEx.GetTypeAssemblyDirectory(dbFactory.GetType());
        //            string assemblyName = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";
        //            //先找请求的程序集目录
        //            string assemblyFullPath = System.IO.Path.Combine(dbPluginDir, assemblyName);
        //            if (File.Exists(assemblyFullPath))
        //            {
        //                assembly = Assembly.LoadFrom(assemblyFullPath);
        //            }

        //            if (assembly == null)
        //            {
        //                ZLoger.Info($"加载\"{args.Name}\"失败");
        //            }
        //        }
        //        catch (Exception ex)
        //        {
        //            ZLoger.Error(ex, $"加载\"{args.Name}\"异常");
        //        }
        //    }
        //    else
        //    {
        //        ZLoger.Warn("args.RequestingAssembly == null");
        //    }

        //    return assembly;
        //}

        /// <summary>
        /// 移除数据库实例
        /// </summary>
        /// <param name="dbid">数据库编号ID</param>
        /// <returns>已移除的数据库访问实例</returns>
        public static IDBAccess RemoveDBAccessInstance(int dbid)
        {
            IDBAccess dbAccess;
            if (_dbAccessDic.TryRemove(dbid, out dbAccess))
            {
                dbAccess.Dispose();
                DbConnectionPoolManager.RemoveDbConnectionPool(dbid);
            }

            return dbAccess;
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public static void Release()
        {
            lock (_dicDBAccessLock)
            {
                foreach (var dbAccess in _dbAccessDic.Values)
                {
                    dbAccess.Dispose();
                }
            }

            DbConnectionPoolManager.ReleaseDbConnection();
        }

        /// <summary>
        /// 获取字段值格式化对象
        /// </summary>
        /// <param name="config">数据配置</param>
        /// <returns></returns>
        public static ISqlFieldValueFormator GetFieldValueFormator(DatabaseConfig config)
        {
            return DBFactoryManager.GetSqlFieldValueFormator(config);
        }

        /// <summary>
        /// 加载指定路径数据库插件
        /// </summary>
        /// <param name="pluginAssemblyPath">数据库插件Assembly路径</param>
        public static void LoadDBPlugin(string pluginAssemblyPath)
        {
            DBFactoryManager.LoadDBPlugin(pluginAssemblyPath);
        }
    }
}
